package cn.zwy.实战;

import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.ASM6;
import static org.objectweb.asm.Opcodes.ASTORE;
import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.POP;

import java.lang.reflect.Field;
import java.math.BigDecimal;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;

public class ASMJSONClassVisitor extends ClassVisitor {

    private Class<?> targetClass;

    public ASMJSONClassVisitor(ClassVisitor cv, Class<?> targetClass) {
        super(ASM6, cv);
        this.targetClass = targetClass;
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        // TODO Auto-generated method stub
        super.visit(version, access, name + "_JSON", signature, superName,
                new String[] { Type.getInternalName(ASMJsonInterface.class) });
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        // TODO Auto-generated method stub
        // 删除之前的方法
        System.out.println(name);
        if (!name.equals("<init>")) {
            return null;
        }
        return super.visitMethod(access, name, desc, signature, exceptions);
    }

    @Override
    public void visitEnd() {
        MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "toJSON", "(Ljava/lang/Object;)Ljava/lang/String;", null, null);
        mv.visitCode();

        // 首先将object参数加载到栈顶
        mv.visitVarInsn(ALOAD, 1);
        // 转成对应的类型,放到栈顶
        // 例如: cn/zwy/bean/UserBean
        mv.visitTypeInsn(CHECKCAST, Type.getInternalName(targetClass));
        // 将栈顶的值存贮到第三个变量中
        mv.visitVarInsn(ASTORE, 2);

        // 以下四个指令就是执行StringBuilder sb = new StringBuilder();
        // new StringBuilder 放到栈顶
        mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
        // 复制栈顶元素
        mv.visitInsn(DUP);
        // 执行栈顶元素的构造方法
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
        // 将栈顶的值放到第四个变量中
        mv.visitVarInsn(ASTORE, 3);

        // 将sb加载到栈顶
        mv.visitVarInsn(ALOAD, 3);
        // 栈顶放入字符串 "{"
        mv.visitLdcInsn("{");
        // 执行sb.append(" { ")并将结果放到栈顶
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
                "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
        // 返回的StringBuilder弹出，不做处理
        mv.visitInsn(POP);
        
        Field[] fieldArr = targetClass.getDeclaredFields();
        int len = fieldArr.length;
        for (int index = 0; index < len; index++) {
            Field field = fieldArr[index];
            // 同上注释
            mv.visitVarInsn(ALOAD, 3);
            mv.visitLdcInsn("\"" + field.getName() + "\"");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
                    "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
            mv.visitInsn(POP);

            // 同上注释
            mv.visitVarInsn(ALOAD, 3);
            mv.visitLdcInsn(":");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
                    "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
            mv.visitInsn(POP);

            // 同上注释
            mv.visitVarInsn(ALOAD, 3);
            mv.visitLdcInsn("\"");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
                    "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
            mv.visitInsn(POP);

            // 将sb推到栈顶
            mv.visitVarInsn(ALOAD, 3);
            // CastObject 转换后的对象推送到栈顶
            mv.visitVarInsn(ALOAD, 2);
            // 获取栈顶对象的方法
            if(field.getType() == BigDecimal.class){
                mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(targetClass),
                        "get" 
                        + field.getName().substring(0, 1).toUpperCase() 
                        + field.getName().substring(1),
                        "()"+Type.getDescriptor(field.getType()), false);
                mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false);
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
            }else{
                mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(targetClass),
                        "get" 
                        + field.getName().substring(0, 1).toUpperCase() 
                        + field.getName().substring(1),
                        "()"+Type.getDescriptor(field.getType()), false);
                // 执行append方法
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
                        "("+Type.getDescriptor(field.getType())+")Ljava/lang/StringBuilder;", false);
            }
            mv.visitInsn(POP);

            // 同上
            mv.visitVarInsn(ALOAD, 3);
            mv.visitLdcInsn("\"");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
                    "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
            mv.visitInsn(POP);
            
            if(index != len - 1){
                mv.visitVarInsn(ALOAD, 3);
                mv.visitLdcInsn(",");
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
                        "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
                mv.visitInsn(POP);
            }
        }

        // 同上
        mv.visitVarInsn(ALOAD, 3);
        mv.visitLdcInsn("}");
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
                "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
        mv.visitInsn(POP);
        
        // sb.toString()
        mv.visitVarInsn(ALOAD, 3);
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);

        // 返回栈顶的值
        mv.visitInsn(ARETURN);

        // mv.visitLocalVariable("this", "Lcn/zwy/\u5b9e\u6218/TOJSON;", null,
        // l0, l10, 0);
        // mv.visitLocalVariable("object", "Ljava/lang/Object;", null, l0, l10,
        // 1);
        // mv.visitLocalVariable("userBean", "Lcn/zwy/bean/UserBean;", null, l1,
        // l10, 2);
        // mv.visitLocalVariable("sb", "Ljava/lang/StringBuilder;", null, l2,
        // l10, 3);
        mv.visitMaxs(2, 4);
        mv.visitEnd();

        cv.visitEnd();
    }

}
